package com.fscp.feed.service;

import cn.hutool.core.bean.BeanUtil;
import com.fscp.commons.constant.ApiConstant;
import com.fscp.commons.constant.RedisKeyConstant;
import com.fscp.commons.exception.ParameterException;
import com.fscp.commons.model.domain.ResultInfo;
import com.fscp.commons.model.pojo.Feed;
import com.fscp.commons.model.vo.SignInDinerInfo;
import com.fscp.commons.utils.AssertUtil;
import com.fscp.feed.mapper.FeedMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.LinkedHashMap;
import java.util.List;

@Service
public class FeedService {

    @Value("${service.name.ms-oauth-server}")
    private String oauthServerName;
    @Value("${service.name.ms-follow-server}")
    private String followServerName;
    @Resource
    private RestTemplate restTemplate;
    @Resource
    private FeedMapper feedMapper;
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 获取登录用户信息  --- 同 FollowService
     *
     * @param accessToken
     * @return
     */
    private SignInDinerInfo loadSignInDinerInfo(String accessToken) {
        // 必须登录
        AssertUtil.mustLogin(accessToken);
        String url = oauthServerName + "user/me?access_token={accessToken}";
        ResultInfo resultInfo = restTemplate.getForObject(url, ResultInfo.class, accessToken);
        if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {
            throw new ParameterException(resultInfo.getMessage());
        }
        SignInDinerInfo dinerInfo = BeanUtil.fillBeanWithMap((LinkedHashMap) resultInfo.getData(),
                new SignInDinerInfo(), false);
        return dinerInfo;
    }

    /**
     * 添加 Feed
     *
     * @param feed
     * @param accessToken
     */
    @Transactional(rollbackFor = Exception.class)
    public void create(Feed feed, String accessToken) {

        // 非空校验 Feed 内容不能为空，不能太长
        AssertUtil.isNotEmpty(feed.getContent(), "请输入内容");
        AssertUtil.isTrue(feed.getContent().length() > 255, "输入内容太多，请重新输入");

        // 获取登录用户信息
        SignInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);

        // Feed 关联用户信息
        feed.setFkDinerId(dinerInfo.getId());

        // 添加 Feed
        int count = feedMapper.save(feed);
        AssertUtil.isTrue(count == 0, "添加失败");

        // 推送到粉丝的列表中（推方案：使用用户关联较少的；拉方案：适用于百万千万级博主。） -- 后续这里应该采用异步消息队列解决性能问题
        // 先获取粉丝 id 集合；  远程调用 ms-follow 服务中的接口
        List<Integer> followers = findFollowers(dinerInfo.getId());

        // 推送 Feed （推方案
        long now = System.currentTimeMillis();   // 时间排序作用
        followers.forEach(follower -> {
            String key = RedisKeyConstant.following_feeds.getKey() + followers;
            redisTemplate.opsForZSet().add(key, feed.getId(), now);     // 推荐 即：在 redis 中添加推送 feed 的主键 id
        });
    }

    /**
     * 获取粉丝 id 集合
     *
     * @param dinerId
     * @return
     */
    private List<Integer> findFollowers(Integer dinerId) {

        String url = followServerName + "follower/" + dinerId;
        ResultInfo resultInfo = restTemplate.getForObject(url, ResultInfo.class);

        if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {
            throw new ParameterException(resultInfo.getCode(), resultInfo.getMessage());
        }

        List<Integer> followers = (List<Integer>) resultInfo.getData();
        return followers;
    }


}
